package com.ams.protocol.http;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ams.util.ObjectCache;
import com.ams.util.BufferUtils;

public class DefaultServlet {
    private final Logger logger = LoggerFactory.getLogger(DefaultServlet.class);

    private class MapedFile {
        public long size;
        public String contentType;
        public long lastModified;
        public ByteBuffer data;
    }

    private HttpContext context = null;
    private static ObjectCache<MapedFile> fileCache = new ObjectCache<MapedFile>();

    public DefaultServlet(HttpContext context) {
        this.context = context;
    }

    public void service(HttpRequest req, HttpResponse res) throws IOException {
        String realPath = null;
        try {
            realPath = context.getRealPath(req.getLocation());
        } catch (Exception e) {
            logger.warn(e.getMessage());
        }
        File file = new File(realPath);
        if (!file.exists()) {
            res.setHttpResult(HTTP.HTTP_NOT_FOUND);
            res.flush();
        } else if (!context.securize(file)) {
            res.setHttpResult(HTTP.HTTP_FORBIDDEN);
            res.flush();
        } else {
            if (!writeFile(req.getLocation(), file, req, res)) {
                res.setHttpResult(HTTP.HTTP_INTERNAL_ERROR);
                res.flush();
            }
        }
    }

    private boolean writeFile(String url, File file, HttpRequest req,
            HttpResponse res) {
        boolean result = true;
        try {
            MapedFile mapedFile = fileCache.get(url);
            if (mapedFile == null) {
                // open the resource stream
                mapedFile = new MapedFile();
                mapedFile.lastModified = file.lastModified();
                mapedFile.size = file.length();
                mapedFile.contentType = context.getMimeType(file.getName());
                FileInputStream fis = new FileInputStream(file);
                FileChannel fileChannel = fis.getChannel();
                mapedFile.data = fileChannel.map(FileChannel.MapMode.READ_ONLY,
                        0, fileChannel.size());
                fileChannel.close();
                fis.close();
                fileCache.put(url, mapedFile, 60);
            }

            res.setContentType(mapedFile.contentType);
            res.setLastModified(mapedFile.lastModified);
            res.setHttpResult(HTTP.HTTP_OK);

            // read all bytes and send them
            ByteBuffer data = mapedFile.data.slice();

            // get range data
            int size = data.remaining();
            int start = 0, end = size - 1;
            String range = req.getHeader("range");
            if (range != null) {
                Matcher m = Pattern.compile("bytes=([0-9]*)-([0-9]*)").matcher(
                        range);
                if (m.find()) {
                    start = (m.group(1).length() == 0) ? 0 : Integer.parseInt(m
                            .group(1));
                    end = (m.group(2).length() == 0) ? end : Integer.parseInt(m
                            .group(2));
                }

                res.setHeader(
                        HTTP.HEADER_CONTENT_RANGE,
                        "bytes " + Integer.toString(start) + "-"
                                + Integer.toString(end) + "/"
                                + Integer.toString(size));
                res.setHttpResult(HTTP.HTTP_PARTIAL_CONTENT);
                data = BufferUtils.slice(data, start, end);
            }
            // using transfer encoing
            res.startChunkedTransfer();
            res.flushChunk(data);
            res.endChunkedTransfer();
        } catch (IOException e) {
            result = false;
            logger.warn(e.getMessage());
        }
        return result;
    }

}